package com.caird.pmp.iot.server;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.ByteToMessageDecoder;
import lombok.extern.slf4j.Slf4j;

import java.util.List;

@Slf4j
public class BbFrameDecoder extends ByteToMessageDecoder {

    public final int MIN_FRAME_LENGTH = 15;

    @Override
    protected void decode(ChannelHandlerContext ctx, ByteBuf buf, List<Object> out) throws Exception {

        int index, readerIndex;

        readerIndex = buf.readerIndex();

        //log.info("#####BufSize" + buf.readableBytes());

        byte[] bytes = new byte[buf.readableBytes()];
        buf.getBytes(readerIndex, bytes);

        while (true) {
            if (buf.readableBytes() < MIN_FRAME_LENGTH)
                break;

            //7E头
            index = indexOf(buf, 0, buf.readableBytes(), (byte) 0x7E);

            if (0 != index) {
                if (index > 0)
                    buf.skipBytes(index);
                else
                    buf.skipBytes(buf.readableBytes());
                continue;
            }

            //7E尾
            index = indexOf(buf, 1, buf.readableBytes(), (byte) 0x7E);

            if (index < 0)
                break;

            if (index + 1 < MIN_FRAME_LENGTH) {
                buf.skipBytes(index);
                continue;
            }

            if (!xor(buf, index + 1)) {
                buf.skipBytes(index);
                continue;
            }

            out.add(escape(buf.readBytes(index + 1), index + 1));
        }
    }

    /**
     * 索引函数
     */
    public static int indexOf(ByteBuf buf, int fromIndex, int toIndex, byte value) {
        return buf.indexOf(buf.readerIndex() + fromIndex, buf.readerIndex() + toIndex, value) - buf.readerIndex();
    }

    /**
     * 异或校验
     * buf含7E头和7E尾
     */
    public static boolean xor(ByteBuf buf, int len) {

        int code = 0;

        int rIndex = buf.readerIndex();
        for (int i=1; i<len-1;) {
            if (0x7D == buf.getUnsignedByte(rIndex+i)) {
                switch (buf.getUnsignedByte(rIndex+i+1)) {
                    case 0x01:
                        code ^= 0x7D;
                        break;
                    case 0x02:
                        code ^= 0x7E;
                        break;
                    default:
                        return false;
                }
                i += 2;
            } else {
                code ^= buf.getUnsignedByte(rIndex+i);
                i++;
            }
        }

        return 0 == code;
    }

    /**
     * 转义
     */
    public static ByteBuf escape(ByteBuf buf, int len) {

        ByteBuf newBuf = Unpooled.buffer(len);

        newBuf.writeByte(0x7E);

        int rIndex = buf.readerIndex();
        for (int i=1; i<len-1;) {
            if (0x7D == buf.getUnsignedByte(rIndex+i)) {
                switch (buf.getUnsignedByte(rIndex+i+1)) {
                    case 0x01:
                        newBuf.writeByte(0x7D);
                        break;
                    case 0x02:
                        newBuf.writeByte(0x7E);
                        break;
                    default:
                        break;
                }
                i += 2;
            } else {
                newBuf.writeByte(buf.getByte(rIndex+i));
                i++;
            }
        }

        newBuf.writeByte(0x7E);

        return newBuf;
    }
}
